# -*-coding: utf-8 -*-
"""
作者：TianyiFan
日期：2023-年11-月04日
用途：用SWO完整求解原问题的主流程
"""

import para as params
import numpy as np
import copy
import time
import common_fuc
from submodel import swo_model
from submodel import swo_cplex_model
from submodel import single_model

class solution_record:
    '''
    专门用于存放原模型结果的类
    '''
    def __init__(self):
        self.obj =  params.max_bound
        self.solving_time = 0
        self.gamma_ik = np.zeros([params.V + 2, params.K1], dtype=float)
        self.beta_lik = np.zeros([params.V + 2, params.K1], dtype=float)
        self.beta_uik = np.zeros([params.V + 2, params.K1], dtype=float)
        self.alpha_lih =  np.zeros([params.V + 2, params.H], dtype=float)
        self.alpha_uih =  np.zeros([params.V + 2, params.H], dtype=float)
        self.deta_u_i = np.zeros(params.V+2, dtype=float)
        self.deta_l_i = np.zeros(params.V+2, dtype=float)
        self.abs_ui1 = np.zeros(params.V+2, dtype=float)
        self.abs_li1 = np.zeros(params.V+2, dtype=float)
        self.sum_li1 = np.zeros(params.V+2, dtype=float)
        self.sum_ui1 = np.zeros(params.V+2, dtype=float)
        self.cost_i = np.zeros(params.V+2, dtype=float)
    def cal_cost_i(self):
        '''根据给定的alpha，beta，abs，计算每艘船的成本'''
        self.obj = 0
        for i in range(1, params.V + 1):
            self.deta_l_i[i] = max(self.alpha_lih[i])
            self.deta_u_i[i] = max(self.alpha_uih[i])
            cost1 = params.time_p * (self.deta_l_i[i] + self.deta_u_i[i] )
            cost2 = params.time_u * (self.abs_li1[i] + self.abs_ui1[i])
            cost3 = 0
            for k in range(params.K1):
                cost3 += params.t_uki[k, i] * self.beta_uik[i, k] + params.t_lki[k, i] * self.beta_lik[i, k]
            self.cost_i[i] = cost1 + cost2 + cost3
            self.obj+=self.cost_i[i]

class swo_algo_fuc:
    def __init__(self):
        self.solution_rd = solution_record()
        self.solving_time = 0
        self.obj = params.max_bound
        self.time_change = 0
    def sequence_stratege_2(self):
        '''
        序列优先性决策策略2：根据total_handle的冲突程度，按照冲突程度，对不同的船进行赋值，根据其[冲突程度、总量]进行降序排序，需要使用dataframe
        :return:
        '''
        # 注意索引：参数数组，均从1开始索引，和dataframe相关的索引，均从0开始索引，因此参数数组中填i+1,dataframe中填i
        temp_data = copy.deepcopy(params.test_data.data)
        conflict_value = np.zeros(params.V,dtype=int)
        for i in range(0,params.V):
            for t in temp_data.iloc[i]['handle_list']:
                conflict_value[i]+= params.total_handle[t]  # 累加当期的冲突度
        temp_data['conflict_value'] = conflict_value
        # print('排序前的序列')
        # common_fuc.print_pd(temp_data)
        temp_data.sort_values(by=['conflict_value','num_container'],ascending=[False,False],inplace=True)
        # print('排序后的序列')
        decision_seqence = list(temp_data['vessel_idx'])
        # print(decision_seqence)
        return decision_seqence,temp_data

    def swo_iteration(self,sequence_data,solver="gurobi"):
        '''
        SWO迭代求解主函数
        :return:
        '''
        DS = params.search_DS  # 单次求解队列的长度
        iteration_num = max(params.V - DS+1, 1)
        time_change = []
        # N从0开始，DS的值即代表每次求解中的数量
        if solver == "gurobi":
            swo_submodel_gurobi = swo_model(sequence_data, self.solution_rd)  # 初始化模型和全局记录的参数
            vessel_list = list(sequence_data['vessel_idx'])
            for N in range(iteration_num):
                print("第 {} 次迭代开始，此时模型中共有 {} 艘船".format(N+1, N + DS))
                t111 = time.time()
                swo_submodel_gurobi.operate_model(N)
                t222 = time.time()
                time_change.append(round(t222 - t111, 2))
                print("SWO_model 第 {} 次迭代的求解时间={:.2f} s".format(N+1, t222 - t111))
                # todo 基于前N+DS艘船，求解原模型，需要注意
                if N < params.V - DS:   # 只更新第N艘船的决策结果
                    # todo 更新在sequence序列中第N个位置的船，在self.solution_rd中的值
                    vessel_idx = sequence_data.iloc[N]['vessel_idx']
                    self.solution_rd.abs_ui1[vessel_idx] = swo_submodel_gurobi.abs_ui1[N].x
                    self.solution_rd.abs_li1[vessel_idx] = swo_submodel_gurobi.abs_li1[N].x
                    for k in range(params.K1):
                        if swo_submodel_gurobi.gamma_ik[N, k].x > 0.5:
                            self.solution_rd.gamma_ik[vessel_idx, k] = swo_submodel_gurobi.gamma_ik[N, k].x
                            self.solution_rd.beta_lik[vessel_idx, k] = swo_submodel_gurobi.beta_lik[N, k].x
                            self.solution_rd.beta_uik[vessel_idx, k] = swo_submodel_gurobi.beta_uik[N, k].x
                    for h in range(params.H):
                        self.solution_rd.alpha_lih[vessel_idx, h] = swo_submodel_gurobi.alpha_lih[N, h].x
                        self.solution_rd.alpha_uih[vessel_idx, h] = swo_submodel_gurobi.alpha_uih[N, h].x
                else:
                    for vessel in range(params.V - DS, params.V):
                        vessel_idx = vessel_list[vessel]
                        self.solution_rd.abs_ui1[vessel_idx] = swo_submodel_gurobi.abs_ui1[vessel].x
                        self.solution_rd.abs_li1[vessel_idx] = swo_submodel_gurobi.abs_li1[vessel].x
                        for k in range(params.K1):
                            if swo_submodel_gurobi.gamma_ik[vessel,k].x>0.5:
                                self.solution_rd.gamma_ik[vessel_idx, k] = 1
                                self.solution_rd.beta_lik[vessel_idx, k] = swo_submodel_gurobi.beta_lik[vessel, k].x
                                self.solution_rd.beta_uik[vessel_idx, k] = swo_submodel_gurobi.beta_uik[vessel, k].x
                        for h in range(params.H):
                            self.solution_rd.alpha_lih[vessel_idx, h] = swo_submodel_gurobi.alpha_lih[vessel, h].x
                            self.solution_rd.alpha_uih[vessel_idx, h] = swo_submodel_gurobi.alpha_uih[vessel, h].x
        else:
            swo_submodel_cplex = swo_cplex_model(sequence_data, self.solution_rd)  # 初始化模型和全局记录的参数
            vessel_list = list(sequence_data['vessel_idx'])
            for N in range(iteration_num):
                print("---------------------------------------------------------------------------")
                print("\n第 {} 次迭代开始，此时模型中共有 {} 艘船".format(N + 1, N + DS))
                t111 = time.time()
                swo_submodel_cplex.operate_model(N)  # 滚动求解模型，并将第N艘船的解，赋值给solution_rd
                t222 = time.time()
                time_change.append(round(t222 - t111, 2))
                print("SWO_model 第 {} 次迭代的求解时间={:.2f} s".format(N + 1, t222 - t111))
                if N < params.V - DS:  # 只更新第N艘船的决策结果
                    # todo 更新在sequence序列中第N个位置的船，在self.solution_rd中的值
                    vessel_idx = vessel_list[N]
                    self.solution_rd.abs_ui1[vessel_idx] = swo_submodel_cplex.swo_record.abs_ui1[vessel_idx]
                    self.solution_rd.abs_li1[vessel_idx] = swo_submodel_cplex.swo_record.abs_li1[vessel_idx]
                    for k in range(params.K1):
                        if swo_submodel_cplex.swo_record.gamma_ik[vessel_idx, k] > 0.5:
                            self.solution_rd.gamma_ik[vessel_idx, k] = swo_submodel_cplex.swo_record.gamma_ik[vessel_idx, k]
                            self.solution_rd.beta_lik[vessel_idx, k] = swo_submodel_cplex.swo_record.beta_lik[vessel_idx, k]
                            self.solution_rd.beta_uik[vessel_idx, k] = swo_submodel_cplex.swo_record.beta_uik[vessel_idx, k]
                    for h in range(params.H):
                        self.solution_rd.alpha_lih[vessel_idx, h] = swo_submodel_cplex.swo_record.alpha_lih[vessel_idx, h]
                        self.solution_rd.alpha_uih[vessel_idx, h] = swo_submodel_cplex.swo_record.alpha_uih[vessel_idx, h]
                else:
                    for vessel in range(params.V - DS, params.V):
                        vessel_idx = vessel_list[vessel]
                        # print("当前赋值：轮船-{} 的结果".format(vessel_idx))
                        self.solution_rd.abs_ui1[vessel_idx] = swo_submodel_cplex.swo_record.abs_ui1[vessel_idx]
                        self.solution_rd.abs_li1[vessel_idx] = swo_submodel_cplex.swo_record.abs_li1[vessel_idx]
                        for k in range(params.K1):
                            if swo_submodel_cplex.swo_record.gamma_ik[vessel_idx, k] > 0.5:

                                self.solution_rd.gamma_ik[vessel_idx, k] = 1
                                self.solution_rd.beta_lik[vessel_idx, k] = swo_submodel_cplex.swo_record.beta_lik[vessel_idx, k]
                                self.solution_rd.beta_uik[vessel_idx, k] = swo_submodel_cplex.swo_record.beta_uik[vessel_idx, k]
                            else:
                                self.solution_rd.gamma_ik[vessel_idx, k] = 0
                                self.solution_rd.beta_lik[vessel_idx, k] = 0
                                self.solution_rd.beta_uik[vessel_idx, k] = 0
                        for h in range(params.H):
                            self.solution_rd.alpha_lih[vessel_idx, h] = swo_submodel_cplex.swo_record.alpha_lih[vessel_idx, h]
                            self.solution_rd.alpha_uih[vessel_idx, h] = swo_submodel_cplex.swo_record.alpha_uih[vessel_idx, h]
        self.solution_rd.cal_cost_i()  # 更新其成本
        self.obj = self.solution_rd.obj
        self.time_change = time_change
        # 将每艘船的信息按照当前序列，合并到结果中
        gamma_ik = []
        for vessel in list(sequence_data['vessel_idx']):
            temp_solution = []
            for k in range(params.K1):
                if self.solution_rd.gamma_ik[vessel, k] > 0.5:
                    temp_solution.append(k)
            gamma_ik.append(temp_solution)
        sequence_data['solution'] = gamma_ik

    def update_rmp_plan_swo(self, base, plan_index, solution_rd):
        '''
        基于输入的result_solution的结果和相应的计划号，更新base中RMP计划池
        :param base:
        :param plan_index:
        :param solution_record: 存放原问题所有解的类
        :return:
        '''
        # 更新A_pi_k 与 B_pig
        for v in range(1, params.V + 1):
            for k in range(params.K1):
                g_index = params.subblock_info[k].blockbelong
                if solution_record.gamma_ik[v, k] > 0.5:
                    base.A_pi_k[v, plan_index, k] = 1
                    base.B_pig[v, plan_index, g_index] += 1
        # 更新D_pin
        for v in range(1, params.V + 1):
            for n in range(params.pair_N):
                for k in params.neighbor_pair[n]:
                    if solution_rd.gamma_ik[v, k] > 0.5:
                        base.D_pin[v, plan_index, n] += 1
                        # print(f'轮船-{v} 分配subblock-{k}  对应neibor_pair={params.neighbor_pair[n]} 编号={n}')
        # 更新Fpi_l与更新Fpi_u
        for v in range(1, params.V + 1):
            base.Fpi_l[v, plan_index] = solution_rd.abs_li1[v]
            base.Fpi_u[v, plan_index] = solution_rd.abs_ui1[v]

        # 更新Cost_pi--每艘船初始计划的成本
        check_cost = 0
        for i in range(1, params.V + 1):
            # 堆场装卸成本
            delta_l = max(solution_rd.alpha_lih[i])
            delta_u = max(solution_rd.alpha_uih[i])
            cost1 = params.time_p * (delta_l + delta_u)
            cost2 = params.time_u * (base.Fpi_l[i, plan_index] + base.Fpi_u[i, plan_index])
            cost3 = 0
            for k in range(params.K1):
                cost3 += params.t_uki[k, i] * solution_rd.beta_uik[i, k] + params.t_lki[k, i] * solution_rd.beta_lik[i, k]
            base.Cost_pi[i, plan_index] = cost1 + cost2 + cost3
            check_cost += base.Cost_pi[i, plan_index]

    def swo_main_main(self):
        time_start = time.time()
        # step1: 生出初始的决策序列
        decision_seq, temp_data = common_fuc.sequence_strategy_2(params.test_data.data)
        common_fuc.print_pd(temp_data)
        # todo 优化：评估每艘船的最优成本
        # vessel_k = []
        # cost_v = []
        # for i in range(1,params.V+1):
        #     model_vessel = single_model(i)
        #     model_vessel.build_model()
        #     vessel_k.append(model_vessel.gamma_k)
        #     cost_v.append(model_vessel.obj)
        # for i in range(params.V):
        #     print("vessel-{}  cost={} subblock={}  ".format(i+1,cost_v[i],vessel_k[i]))
        # print("总成本={}".format(sum(cost_v)))
        test_mode = 2
        if test_mode ==1:
            solver = "gurobi"
        else:
            solver = "cplex"
        # step2: 将决策序列代入swo_iteration 求解原模型
        self.swo_iteration(temp_data,solver)
        time_end = time.time()

        # step3: 将最后的结果输出，进行校验
        print("SWO 算法求解结果：")
        common_fuc.print_pd(temp_data)
        print("SWO单次求解时间变化：{}".format(self.time_change))
        print("SWO算法求解耗时：{:.2f}s    求解结果={:.2f}".format(time_end-time_start,self.obj))
        self.solving_time = time_end-time_start





